package org.wzy.spring.framework.beans.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wzy.spring.framework.beans.factory.config.BeanDefinition;
import org.wzy.spring.framework.beans.factory.support.AbstractBeanDefinition;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

/**
 * 模拟 Spring 下的 XmlBeanDefinitionReader, 作为查找、读取、解析xml配置的类
 *
 * @author wzy_h
 * @version v_1.0
 * @date 2018/5/19 23:04
 * @since 1.8
 */
public class BeanDefinitionReader {
    /**
     * 存储被注册过的 Bean Class 名称
     */
    private final List<String> beanDefinitionClasses = new ArrayList<>(10);
    /**
     * 使用 Properties 代替 XML 配置
     */
    private final Properties config = new Properties();
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public BeanDefinitionReader(String... locations) throws FileNotFoundException {
        this.doLoadBeanDefinitions(locations);
        this.doScan(config.getProperty("basePackage"));
    }

    private void doLoadBeanDefinitions(String... locations) {
        // 使用输入流, 读取 properties 文件的内容, 写死为第一个下标
        InputStream resource = this.getClass().getClassLoader().getResourceAsStream(locations[0]);
        try {
            config.load(resource);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != resource) {
                try {
                    resource.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 实际上就是递归扫描指定包下包含指定注解的 Bean, 并保存在 List 中
     *
     * @param packageName 包名
     * @author wzy_h
     * @date 2018/5/19 23:26
     * @since 1.8
     */
    private void doScan(String packageName) throws FileNotFoundException {
        // 简略实现, 直接读取指定包下的所有文件
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        if (null == url) {
            throw new FileNotFoundException("未找到指定路径！");
        }

        File urlFile = new File(url.getFile());
        for (File file : Objects.requireNonNull(urlFile.listFiles())) {
            if (file.isDirectory()) {
                // 如果找到的是文件夹, 则递归调用本方法
                logger.info("doScan() 找到了文件夹, 将继续递归查找... packageName = {}", packageName + "." + file.getName());
                doScan(packageName + "." + file.getName());
            } else {
                logger.info("doScan() 找到了文件, 准备添加至集合中... packageName = {}", packageName + "." + file.getName());
                beanDefinitionClasses.add(packageName + "." + file.getName().replace(".class", ""));
            }
        }
    }

    /**
     * 向 Spring IOC 容器中注册 BeanDefinition, BeanDefinition 只是对配置信息的包装
     *
     * @param className 类名
     * @author wzy_h
     * @date 2018/5/19 23:11
     * @since 1.8
     */
    public BeanDefinition registerBeanDefinition(String className) {
        if (null == className) {
            return null;
        } else if (beanDefinitionClasses.contains(className)) {
            BeanDefinition beanDefinition = new AbstractBeanDefinition();
            beanDefinition.setBeanClassName(className);
            beanDefinition.setFactoryBeanName(
                    lowerFirstChar(className.substring(className.lastIndexOf(".") + 1, className.length())));
            return beanDefinition;
        }
        return null;
    }

    public Properties getConfigurations() {
        return this.config;
    }

    public List<String> getBeanDefinitions() {
        return beanDefinitionClasses;
    }

    /**
     * 将给定字符串首字母转换为小写
     *
     * @author wzy_h
     * @date 2018/5/19 16:03
     * @since 1.8
     */
    private String lowerFirstChar(String originalStr) {
        if (null == originalStr || "".equals(originalStr)) {
            return originalStr;
        }

        char[] array = originalStr.toCharArray();
        array[0] += 32;
        return String.valueOf(array);
    }
}
